#[macro_use]
extern crate cstr;
#[macro_use]
extern crate cpp;
#[macro_use]
extern crate qmetaobject;

use futures::Future;
use qmetaobject::*;

mod catalog_provider_4;
mod qrc;

#[derive(serde_derive::Serialize)]
#[serde(untagged)]
enum GLItem {
    Collection { title: String, items: Vec<GLItem> },
    Content { title: String, body: String },
    Link { title: String, uri: String },
    Error { title: String },
}

trait CatalogProvider: Sync {
    fn load(&self, uri: &str) -> Box<Future<Item = GLItem, Error = String> + Send>;
}

lazy_static! {
    static ref CATALOG_PROVIDER: Box<CatalogProvider + Send> =
        Box::new(catalog_provider_4::CatalogProvider4::new());
}

#[derive(QObject, Default)]
struct GLItemLoader {
    base: qt_base_class!(trait QObject),
    load: qt_method!(fn(&self, uri: String)),
    loaded: qt_signal!(item: String),
    failed: qt_signal!(err: String),
}

impl GLItemLoader {
    fn load(&self, uri: String) {
        println!("load: {}", uri);
        let callback = {
            let ptr = QPointer::from(&*self);
            queued_callback(move |res| {
                if let Some(x) = ptr.as_ref() {
                    match res {
                        Ok(item) => x.loaded(item),
                        Err(err) => x.failed(err),
                    }
                }
            })
        };

        let path = match uri.find('?') {
            Some(idx) => &uri[..idx],
            None => &uri,
        };

        tokio::spawn(
            CATALOG_PROVIDER
                .load(path)
                .and_then(|item| {
                    serde_json::to_string(&item)
                        .map_err(|e| format!("Failed to serialize item: {:?}", e))
                })
                .then(move |res| {
                    callback(res);
                    Ok(())
                }),
        );
    }
}

fn main() {
    tokio::run(futures::lazy(move || {
        unsafe {
            cpp! { {
                #include <QtCore/QCoreApplication>
                #include <QtWebEngine/QtWebEngine>
            }}
            cpp! {[]{
                QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
            }}
        }
        if Err(std::env::VarError::NotPresent) == std::env::var("QT_QUICK_CONTROLS_STYLE") {
            QQuickStyle::set_style("Suru");
        }
        qrc::load();
        qml_register_type::<GLItemLoader>(cstr!("OGLQApp"), 0, 0, cstr!("GLItemLoader"));
        let mut engine = QmlEngine::new();

        unsafe {
            cpp! {[]{
                QtWebEngine::initialize();
            }}
        }

        engine.load_file("qrc:/qml/Main.qml".into());
        engine.exec();

        Ok(())
    }))
}
